/**
 * Create in 2012-9-8 上午10:21:10.
 */
package kit4j.common.io.file;

import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;

import kit4j.util.lang.ArrayUtil;

/**
 * 文件搜索工具类。
 * 
 * @author MiniKnife
 * 
 */
public class FileFinder {

	static class SingletonHolder {

		private static FilenameFilter acceptAllFilenameFilter = new AcceptAllFilenameFilter();

		private static FileFilter acceptAllFileFilter = new AcceptAllFileFilter();

		static FilenameFilter getAcceptAllFilenameFilter() {
			return acceptAllFilenameFilter;
		}

		static FileFilter getAcceptAllFileFilter() {
			return acceptAllFileFilter;
		}
	}

	/**
	 * 返回一个抽象路径名数组，这些路径名表示给定File对象表示的目录中的文件。
	 * 
	 * @param dir
	 *            File对象
	 * @return 抽象路径名数组，这些路径名表示给定File对象表示的目录中的文件和目录。如果目录为空，那么数组也将为空。如果抽象路径名不表示一个目录，或者发生 I/O 错误，则返回 null。
	 * @see File#listFiles()
	 */
	public File[] listFiles(File dir) {
		if (isDirectory(dir)) {
			return dir.listFiles();
		} else {
			return null;
		}
	}

	/**
	 * 判断给定的File对象是否为文件夹。
	 * 
	 * @param f
	 *            要判断的File对象
	 * @return 当且仅当此File对象表示的文件存在且 是一个目录时，返回 true；否则返回 false。
	 */
	private boolean isDirectory(File f) {
		return f != null && f.isDirectory();
	}

	/**
	 * 返回抽象路径名数组，这些路径名表示给定File对象表示的目录中满足指定过滤器的文件和目录。
	 * 
	 * @param dir
	 *            File对象
	 * @param filter
	 *            文件名过滤器
	 * @return 抽象路径名数组，这些路径名表示给定File对象表示的目录中的文件和目录。如果目录为空，那么数组也将为空。如果此抽象路径名不表示一个目录，或者发生 I/O 错误，则返回 null。
	 * @see File#listFiles(FilenameFilter)
	 */
	public File[] listFiles(File dir, FilenameFilter filter) {
		if (isDirectory(dir)) {
			return dir.listFiles(filter);
		} else {
			return null;
		}
	}

	/**
	 * 返回抽象路径名数组，这些路径名表示给定File对象表示的目录中满足指定过滤器的文件和目录。
	 * 
	 * @param dir
	 *            File对象
	 * @param filter
	 *            文件过滤器
	 * @return 抽象路径名数组，这些路径名表示给定File对象表示的目录中的文件和目录。如果目录为空，那么数组也将为空。如果此抽象路径名不表示一个目录，或者发生 I/O 错误，则返回 null。
	 * @see File#listFiles(FilenameFilter)
	 */
	public File[] listFiles(File dir, FileFilter filter) {
		if (isDirectory(dir)) {
			return dir.listFiles(filter);
		} else {
			return null;
		}
	}

	/**
	 * 返回一个抽象路径名数组，这些路径名表示给定File对象表示的目录中的所有文件(包括子文件夹中的)。
	 * 
	 * @param dir
	 *            File对象
	 * @return 抽象路径名数组，这些路径名表示给定File对象表示的目录中的文件和目录。如果目录为空，那么数组也将为空。如果抽象路径名不表示一个目录，或者发生 I/O 错误，则返回 null。
	 */
	public File[] deepListFiles(File dir) {
		if (isDirectory(dir)) {
			File[] subFiles = dir.listFiles();

			for (File sf : subFiles) {
				if (sf.isDirectory()) {
					subFiles = ArrayUtil.append(subFiles, deepListFiles(sf));
				}
			}

			return subFiles;
		} else {
			return null;
		}
	}

	/**
	 * 返回抽象路径名数组，这些路径名表示给定File对象表示的目录中满足指定过滤器的文件和目录。
	 * 
	 * @param dir
	 *            File对象
	 * @param filter
	 *            文件名过滤器
	 * @return 抽象路径名数组，这些路径名表示给定File对象表示的目录中的文件和目录。如果目录为空，那么数组也将为空。如果此抽象路径名不表示一个目录，或者发生 I/O 错误，则返回 null。
	 * @see File#listFiles(FilenameFilter)
	 */
	public File[] deepListFiles(File dir, FilenameFilter filter) {
		return deepListFiles(dir, true, filter);
	}

	/**
	 * 返回抽象路径名数组，这些路径名表示给定File对象表示的目录中满足指定过滤器的文件和目录。
	 * 
	 * @param dir
	 *            File对象
	 * @param isRoot
	 *            是否是遍历的根目录
	 * @param filter
	 *            文件名过滤器
	 * @return 抽象路径名数组，这些路径名表示给定File对象表示的目录中的文件和目录。如果目录为空，那么数组也将为空。如果此抽象路径名不表示一个目录，或者发生 I/O 错误，则返回 null。
	 * @see File#listFiles(FilenameFilter)
	 */
	private File[] deepListFiles(File dir, boolean isRoot, FilenameFilter filter) {
		if (isDirectory(dir)) {
			File[] subFiles = dir.listFiles();

			if (subFiles != null) {

				for (File sf : subFiles) {
					if (sf.isDirectory()) {
						subFiles = ArrayUtil.append(subFiles, deepListFiles(sf, false, filter));
					}
				}

				if (isRoot) {
					for (int i = 0; i < subFiles.length; ++i) {
						if (!filter.accept(dir, subFiles[i].getName())) {
							subFiles[i] = null;
						}
					}
					
					subFiles = ArrayUtil.eraseNull(subFiles);
				}

				return subFiles;
			}
		}

		return null;
	}

	/**
	 * 返回抽象路径名数组，这些路径名表示给定File对象表示的目录中满足指定过滤器的文件和目录。
	 * 
	 * @param dir
	 *            File对象
	 * @param filter
	 *            文件过滤器
	 * @return 抽象路径名数组，这些路径名表示给定File对象表示的目录中的文件和目录。如果目录为空，那么数组也将为空。如果此抽象路径名不表示一个目录，或者发生 I/O 错误，则返回 null。
	 * @see File#listFiles(FilenameFilter)
	 */
	public File[] deepListFiles(File dir, FileFilter filter) {
		return deepListFiles(dir, true, filter);
	}

	/**
	 * 返回抽象路径名数组，这些路径名表示给定File对象表示的目录中满足指定过滤器的文件和目录。
	 * 
	 * @param dir
	 *            File对象
	 * @param isRoot
	 *            是否是遍历的根目录
	 * @param filter
	 *            文件过滤器
	 * @return 抽象路径名数组，这些路径名表示给定File对象表示的目录中的文件和目录。如果目录为空，那么数组也将为空。如果此抽象路径名不表示一个目录，或者发生 I/O 错误，则返回 null。
	 * @see File#listFiles(FilenameFilter)
	 */
	private File[] deepListFiles(File dir, boolean isRoot, FileFilter filter) {
		if (isDirectory(dir)) {
			File[] subFiles = dir.listFiles();

			if (subFiles != null) {

				for (File sf : subFiles) {
					if (sf.isDirectory()) {
						subFiles = ArrayUtil.append(subFiles, deepListFiles(sf, false, filter));
					}
				}

				if (isRoot) {
					for (int i = 0; i < subFiles.length; ++i) {
						if (!filter.accept(subFiles[i])) {
							subFiles[i] = null;
						}
					}
					
					subFiles = ArrayUtil.eraseNull(subFiles);
				}

				return subFiles;
			}
		}

		return null;
	}

	/**
	 * 检阅给定的文件夹中的文件(夹)。
	 * 
	 * @param dir
	 *            要检阅的文件夹
	 * @param looker
	 *            检阅者对象
	 */
	public void lookupFiles(File dir, FileLooker looker) {
		listFiles(dir, new FileFilterAdapter(SingletonHolder.getAcceptAllFileFilter(), looker));
	}

	/**
	 * 检阅给定的文件夹中符合满足过滤器的文件(夹)。
	 * 
	 * @param dir
	 *            要检阅的文件夹
	 * @param filter
	 *            文件名过滤器
	 * @param looker
	 *            检阅者对象
	 */
	public void lookupFiles(File dir, FilenameFilter filter, FileLooker looker) {
		listFiles(dir, new FilenameFilterAdapter(filter, looker));
	}

	/**
	 * 检阅给定的文件夹中符合满足过滤器的文件(夹)。
	 * 
	 * @param dir
	 *            要检阅的文件夹
	 * @param filter
	 *            文件过滤器
	 * @param looker
	 *            检阅者对象
	 */
	public void lookupFiles(File dir, FileFilter filter, FileLooker looker) {
		listFiles(dir, new FileFilterAdapter(filter, looker));
	}

	/**
	 * 检阅给定的文件夹中的所有文件(夹)，包括子文件夹中的所有文件(夹)。
	 * 
	 * @param dir
	 *            要检阅的文件夹
	 * @param looker
	 *            检阅者对象
	 */
	public void deepLookupFiles(File dir, FileLooker looker) {
		deepListFiles(dir, new FileFilterAdapter(SingletonHolder.getAcceptAllFileFilter(), looker));
	}

	/**
	 * 检阅给定的文件夹中所有符合满足过滤器的文件(夹)，包括子文件夹中的所有文件(夹)。
	 * 
	 * @param dir
	 *            要检阅的文件夹
	 * @param filter
	 *            文件名过滤器
	 * @param looker
	 *            检阅者对象
	 */
	public void deepLookupFiles(File dir, FilenameFilter filter, FileLooker looker) {
		deepListFiles(dir, new FilenameFilterAdapter(filter, looker));
	}

	/**
	 * 检阅给定的文件夹中所有符合满足过滤器的文件(夹)，包括子文件夹中的所有文件(夹)。
	 * 
	 * @param dir
	 *            要检阅的文件夹
	 * @param filter
	 *            文件过滤器
	 * @param looker
	 *            检阅者对象
	 */
	public void deepLookupFiles(File dir, FileFilter filter, FileLooker looker) {
		deepListFiles(dir, new FileFilterAdapter(filter, looker));
	}

}
